#ifndef _DISK_ABSTRACTION_H
#define _DISK_ABSTRACTION_H

#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestAssert.h>
#include<stdio.h>
#include "SuperBlockData.h"
#include "Exception.h"
#include "Auxilliary.h"
using std::cout;
using std::endl;

typedef unsigned int BlockId;
typedef unsigned int flag;
class DiskAbstraction
{
public:
	DiskAbstraction(string& fileName, unsigned int blockSize, unsigned int diskSize);
	~DiskAbstraction(void);
	void read(BlockId id, size_t baseSize, void* buffer);
	void write(BlockId id, void* data);
	unsigned int getTotalBlockCount();
	unsigned int getDiskSize();
	unsigned int getBlockSize();
	void seekAndRead(BlockId id, size_t seekSize, unsigned int offset, void* container, size_t containerSize);
	void seekAndWrite(BlockId id, size_t seekSize, unsigned int offset, void* value, size_t containerSize);
	bool isNew();
private:
	string _fileName;
	unsigned int _blockSize;
	unsigned int _diskSize;
	unsigned int _totalBlockCount;
	bool _isNew;
};

class DiskAbstractionTestCase : public CPPUNIT_NS::TestCase {
	CPPUNIT_TEST_SUITE(DiskAbstractionTestCase);
	CPPUNIT_TEST_EXCEPTION(testWrongBufferSize, Exception);
	CPPUNIT_TEST_EXCEPTION(testWrongDiskSize, Exception);
	CPPUNIT_TEST_EXCEPTION(testWrongReadBlockId, Exception);
	CPPUNIT_TEST_EXCEPTION(testWrongWriteBlockId, Exception);
	CPPUNIT_TEST(testExistingDiskConstructor);
	CPPUNIT_TEST(testNewDiskCreation);
	CPPUNIT_TEST(testNewDiskReadWrite);
	CPPUNIT_TEST(testExistingDisk);
	CPPUNIT_TEST(testReadWriteWithOffset);
	/*CPPUNIT_TEST(testReadWithOffset);*/
	//CPPUNIT_TEST();
	CPPUNIT_TEST_SUITE_END();

public:
	void setUp(void) {
		cout<<endl;
	}
	void tearDown(void) {
		remove( "FILE_SYS" );
	}

protected:
	void testWrongBufferSize(void) {
		string fs("FILE_SYS");
		DiskAbstraction da(fs,10,1000);
	}
	void testWrongDiskSize(void) {
		string fs("FILE_SYS");
		DiskAbstraction da(fs,64,128);
	}
	void testWrongReadBlockId(void){
		string fs("FILE_SYS");
		DiskAbstraction da(fs,64,650);
		da.read(64, sizeof(int),NULL);
	}
	void testWrongWriteBlockId(void){
		string fs("FILE_SYS");
		DiskAbstraction da(fs,64,650);
		int testInt(3);
		da.write(64,(void*)&testInt);
	}
	void testNewDiskCreation(void) {
		string fs("FILE_SYS");
		DiskAbstraction da(fs,64,650);
		CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong number of blocks created",(unsigned int)10,da.getTotalBlockCount());
		CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong disk size created",(unsigned int)640,da.getDiskSize());
		CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong block size created",(unsigned int)64,da.getBlockSize());
	}
	void testNewDiskReadWrite(void) {
		string fs("FILE_SYS");
		DiskAbstraction da(fs,64,650);
		unsigned int res[64];
		da.read(0,sizeof(unsigned int),res);
		CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong disk size on file system",(unsigned int)640,res[0]);
		CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong block size on file system",(unsigned int)64,res[1]);
		unsigned int arr[16];
		for(unsigned int i=0;i<16;i++){
			arr[i]=i;
		}
		da.write(1,arr);
		da.read(1,sizeof(unsigned int),res);
		for(unsigned int i=0;i<16;i++){
			CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong read value",i,res[i]);
		}
		string asciiStr("test ascii");
		char testAscii[64];
		Auxilliary::toCharArray(asciiStr,da.getBlockSize(),testAscii);
		da.write(3,(void*)testAscii);
		char asciiBuffer[64];
		da.read(3,sizeof(char),asciiBuffer);
		string readAscii(asciiBuffer);
		CPPUNIT_ASSERT_EQUAL_MESSAGE("Ascii read/write incorrect",asciiStr,readAscii);
	}
	void testExistingDisk(void) {
		string fs("FILE_SYS");
		DiskAbstraction da(fs,64,650);
		string asciiStr("test ascii");
		char testAscii[64];
		Auxilliary::toCharArray(asciiStr,da.getBlockSize(),testAscii);
		da.write(3,(void*)testAscii);
		DiskAbstraction daExisting(fs,64,650);
		CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong number of blocks read",(unsigned int)10,daExisting.getTotalBlockCount());
		CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong disk size read",(unsigned int)640,daExisting.getDiskSize());
		CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong block size read",(unsigned int)64,daExisting.getBlockSize());	
		char asciiBuffer[64];
		da.read(3,sizeof(char),asciiBuffer);
		string readAscii(asciiBuffer);
		CPPUNIT_ASSERT_EQUAL_MESSAGE("Ascii read/write incorrect",asciiStr,readAscii);
	}
	void testReadWriteWithOffset(void) {
		string fs("FILE_SYS");
		DiskAbstraction da(fs,64,650);
		unsigned int arr[16];
		for(unsigned int i=0;i<16;i++){
			arr[i]=i;
		}
		da.write(1,arr);
		unsigned int read(0);
		da.seekAndRead(1,sizeof(unsigned int),4,(void*)&read,sizeof(unsigned int));
		CPPUNIT_ASSERT_EQUAL_MESSAGE("Ascii read/write incorrect",(unsigned int)4,read);
		read=2;
		da.seekAndWrite(1,sizeof(unsigned int),4,&read,sizeof(unsigned int));
		da.seekAndRead(1,sizeof(unsigned int),4,(void*)&read,sizeof(unsigned int));
		CPPUNIT_ASSERT_EQUAL_MESSAGE("Ascii read/write incorrect",(unsigned int)2,read);
	}
	void testExistingDiskConstructor(void) {
		string fs("FILE_SYS");
		DiskAbstraction da(fs,64,650);	
		try{	
			DiskAbstraction daExisting(fs,0,0);
		}catch(Exception ex){
			CPPUNIT_FAIL("Disk creation should complete normaly even with wrong parameters");
		}
	}
	/*void (void) {
	std::cout << "Hello, world!" << std::endl;
	}
	void (void) {
	std::cout << "Hello, world!" << std::endl;
	}*/
};


#endif
